// Amaze (StateB.java)
// author: Carmelo Mirko Musumeci <mirkochip@criluge.it>

package dk.sdu.amaze;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.geom.GeneralPath;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Locale;

import TUIO.TuioCursor;

/*
 * Describes second game screen: list of unlocked levels and best times
 */
public class StateB extends State {
	private static final long serialVersionUID = 1L;

	Sound choose_level;
	Thread t_choose_level;

	TuioCursor activeTuioCursor;

	private ArrayList<Level> levels;
	private int[] bestTimes;
	private int selectedLevelFieldNumber = 0;


	// if level is null, the last unlocked level will be selected
	public StateB(Amaze context) {
		super(context);

		levels = ctx.getLevels(); // ctx is a protected attribute from super
		// class State, now i have the levels
		bestTimes = loadBestTimes();
	}


	public void onNext() {
		Level level = levels.get(selectedLevelFieldNumber);
		StateC stateC = new StateC(ctx, level);
		ctx.setState(stateC);
	}

	public void onExit() {
		System.exit(0);
	}

	private int[] loadBestTimes() {
		int[] bestTimes = new int[10];

		for (int i = 0; i < 10; i++)
			bestTimes[i] = -1; // -1 for not loaded levels

		for (int i = 0; i < 10; i++) {
			Level level;

			try {
				level = levels.get(i);
				bestTimes[i] = ctx.getBestTime(level.getId()); // 0 for locked
				// levels, > 0
				// for unlocked
				// levels
			} catch (IndexOutOfBoundsException e) {
				// Error, less levels loaded than level fields on the screen
				break;
			}
		}

		return bestTimes;
	}

	private boolean isUnlocked(int levelNumber) {
		if (levelNumber == 0)
			return true;

		if (levelNumber > 0 && levelNumber < 10) {
			if (bestTimes[levelNumber] > 0 || bestTimes[levelNumber - 1] > 0)
				return true;
		}
		return false;
	}

	public void selectLevel(Level level) {
		int levelNumber = ctx.getLevelNumber(level);

		if (levelNumber >= 0)
			selectedLevelFieldNumber = levelNumber;
	}

	public void selectNextLevel() {
		for (int i = 0; i < 10 && isUnlocked(i); i++) {
			selectedLevelFieldNumber = i;
		}
	}

	public void hitScore(Level level, int newTime) {
		int currentBest = ctx.getBestTime(level.getId());

		if (currentBest == 0 || newTime < currentBest) {
			ctx.setBestTime(level.getId(), newTime);
			bestTimes = loadBestTimes();
		}

		if (currentBest==0) {
			selectNextLevel(); // selects freshly unlocked level
		} else {
			selectLevel(level);
		}

		//repaint();
	}

	private Point getFieldCoordinates(TuioCursor tcur) {
		// 48 individualble locations on the screen (7 columns and 5 rows)
		return new Point((int) (tcur.getX() * 7), (int) (tcur.getY() * 5));
	}

	// returns number of level field for selected point, 0 - 9 !
	private int getLevelFieldNumber(Point p) {
		if (p.getX() == 0.0) {
			return (int) p.getY();
		} else if (p.getX() == 6.0) {
			return (int) p.getY() + 5;
		}
		return -1; // selected point is not a level selection field
	}

	@Override
	public void paint(Graphics g) {
		Graphics2D g2 = (Graphics2D) g;
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);
		g2.setRenderingHint(RenderingHints.KEY_RENDERING,
				RenderingHints.VALUE_RENDER_QUALITY);

		// painting background
		((Graphics2D) g2).setPaint(new GradientPaint(800, 600, Color.red, 0, 0,
				Color.green));
		g2.fillRect(0, 0, getWidth(), getHeight());

		g2.setColor(Color.black);

		int squareSize = 80;
		int columns = 7;
		int rows = 5;

		/* Play string */
		FontMetrics fmPlay = g2.getFontMetrics(new Font(g2.getFont()
				.getFontName(), Font.BOLD, 20));
		String playString = "NEXT";
		// Find the size of string in font in the current Graphics context g2
		java.awt.geom.Rectangle2D rect1 = fmPlay
				.getStringBounds(playString, g2);
		int textWidth1 = (int) (rect1.getWidth());
		g2.setFont(new Font(g2.getFont().getFontName(), Font.BOLD, 20));
		g2.setColor(Color.green);
		g2.drawString(playString, ((800 - textWidth1) / 2), 230);// draw the
		// string in
		// center

		/* Quit string */
		FontMetrics fmQuit = g2.getFontMetrics(new Font(g2.getFont()
				.getFontName(), Font.BOLD, 20));
		String quitString = "QUIT";
		// Find the size of string in font in the current Graphics context g2
		java.awt.geom.Rectangle2D rect2 = fmQuit
				.getStringBounds(quitString, g2);
		int textWidth2 = (int) (rect2.getWidth());
		g2.setFont(new Font(g2.getFont().getFontName(), Font.BOLD, 20));
		g2.setColor(Color.red);
		g2.drawString(quitString, ((800 - textWidth2) / 2), 390); // draw the
		// string in
		// center

		/* draw "choose the level" string */
		/*
		 * FontMetrics fm = g2.getFontMetrics(new
		 * Font(g2.getFont().getFontName(),Font.BOLD,20)); String
		 * levelChooseString = "Choose and then play the level."; // Find the
		 * size of string in font in the current Graphics context g2
		 * java.awt.geom.Rectangle2D rect =
		 * fm.getStringBounds(levelChooseString, g2); int textWidth =
		 * (int)(rect.getWidth()); g2.setFont(new
		 * Font(g2.getFont().getFontName(),Font.BOLD,20));
		 * g2.setColor(Color.YELLOW); g2.drawString(levelChooseString, ((800 -
		 * textWidth) / 2), 450);
		 */

		/* setting grid */
		GeneralPath gridPath = new GeneralPath();

		for (int i = 1; i < columns; i++) {
			gridPath.moveTo(i * squareSize, 0);
			gridPath.lineTo(i * squareSize, rows * squareSize);
		}

		for (int i = 1; i < rows; i++) {
			gridPath.moveTo(0, i * squareSize);
			gridPath.lineTo(columns * squareSize, i * squareSize);
		}

		/* setting border */
		GeneralPath borderPath = new GeneralPath();

		int right_x = columns * squareSize;
		int bottom_y = rows * squareSize;

		borderPath.moveTo(0, 0);
		borderPath.lineTo(0, bottom_y);
		borderPath.moveTo(right_x, 0);
		borderPath.lineTo(right_x, bottom_y);

		borderPath.moveTo(0, 0);
		borderPath.lineTo(right_x, 0);
		borderPath.moveTo(0, bottom_y);
		borderPath.lineTo(right_x, bottom_y);

		/* game board translation */
		Rectangle2D borderPathBounds = borderPath.getBounds();

		g2.translate(getWidth() / 2 - borderPathBounds.getWidth() / 2,
				getHeight() / 2 - borderPathBounds.getHeight() / 2);

		// paint level fields
		for (int levelField = 0; levelField < 10; levelField++) {
			if (levelField == selectedLevelFieldNumber) {
				g2.setPaint(Color.orange);
			} else {
				if (isUnlocked(levelField)) {
					g2.setPaint(new Color(1.0f, 1.0f, 1.0f, .25f));
				} else {
					g2.setPaint(Color.gray);
				}
			}

			if (levelField < 5) {
				g2.fillRect(0, levelField * 80, squareSize, squareSize);
			} else {
				g2.fillRect(480, (levelField - 5) * 80, squareSize, squareSize);
			}
		}

		/* draw level numbers and best times on the square */

		// Find the size of string in font in the current Graphics context g2
		FontMetrics fmL = g2.getFontMetrics(new Font(
				g2.getFont().getFontName(), Font.BOLD, 20));
		FontMetrics fmT = g2.getFontMetrics(new Font(
				g2.getFont().getFontName(), Font.BOLD, 10));

		for (int levelField = 0; levelField < 10; levelField++) {
			if (isUnlocked(levelField)) {
				g2.setColor(Color.black);
			} else {
				g2.setColor(Color.white);
			}

			String levelString = String.valueOf(levelField + 1);
			String bestTimeString;

			int bestTime = bestTimes[levelField];

			if (bestTime > 0) {
				bestTimeString = String.format(Locale.US, "%.2f",
						(double) bestTime / 1000)
						+ " s";
			} else if (bestTime == 0) {
				if (isUnlocked(levelField))
					bestTimeString = "Unlocked";
				else
					bestTimeString = "Locked";
			} else
				bestTimeString = "Unavailable";

			// put the string in a rectangle, evaluation of the size
			java.awt.geom.Rectangle2D rectL = fmL.getStringBounds(levelString,
					g2);
			java.awt.geom.Rectangle2D rectT = fmT.getStringBounds(
					bestTimeString, g2);

			int textWidthL = (int) (rectL.getWidth());
			int textWidthT = (int) (rectT.getWidth());

			int side;
			int row;

			if (levelField < 5) {
				side = 0;
				row = levelField;
			} else {
				side = 480;
				row = levelField - 5;
			}

			g2.setFont(new Font(g2.getFont().getFontName(), Font.BOLD, 20));
			g2.drawString(levelString, ((squareSize - textWidthL) / 2) + side,
					(squareSize / 2 + (squareSize * row))); // Draw the Level
			// string

			g2.setFont(new Font(g2.getFont().getFontName(), Font.BOLD, 10));
			g2.drawString(bestTimeString, ((squareSize - textWidthT) / 2)
					+ side, (squareSize / 2 + (squareSize * row)) + 20); // Draw
			// the
			// string
			// of
			// the
			// best
			// time
		}

		/* draw grid */
		g2.setPaint(Color.black);
		g2.draw(gridPath);

		/* draw border */
		g2.setStroke(new BasicStroke(2.0f));
		g2.setPaint(Color.black);
		g2.draw(borderPath);

	}

	@Override
	public void addTuioCursor(TuioCursor tcur) {

		if (activeTuioCursor != null)
			return;

		activeTuioCursor = tcur;

		Point touchedFieldCoordinates = getFieldCoordinates(tcur);
		// System.out.println("(" + touchedFieldCoordinates.getX() + ", " +
		// touchedFieldCoordinates.getY() + ")");

		int levelFieldNumber = getLevelFieldNumber(touchedFieldCoordinates);

		if (levelFieldNumber >= 0) {
			if (isUnlocked(levelFieldNumber)) {
				selectedLevelFieldNumber = levelFieldNumber;

				choose_level = new Sound("move.wav");
				t_choose_level = new Thread(choose_level);
				t_choose_level.start();
			} else {
				choose_level = new Sound("chooseL.wav");
				t_choose_level = new Thread(choose_level);
				t_choose_level.start();
			}
		} else { // it's not a level field (put other sound?)

			// check if it's next or quit
			int column = (int) touchedFieldCoordinates.getX();
			int row = (int) touchedFieldCoordinates.getY();

			if (column == 3) {
				if (row == 1)
					onNext();
				else if (row == 3)
					onExit();
			}
		}

	}

	@Override
	public void removeTuioCursor(TuioCursor tcur) {
		if (activeTuioCursor == tcur) {
			activeTuioCursor = null;
		}
	}

	@Override
	public void updateTuioCursor(TuioCursor tcur) {
		// we don't need of this method on this state
	}
}
